#include <grace/application.h>
#include <grace/filesystem.h>

class masterconf : public application
{
public:
	masterconf (void) : application ("com.openpanel.tools.postfix-masterconf")
	{
		opt = $("-c", $("long", "--conf")) ->
			  $("--conf",
			      $("argc", 1) ->
			      $("default", "/etc/postfix/master.cf")
			   );
	}
	
	~masterconf (void)
	{
	}
	
	value *parsemastercf (const string &path)
	{
		returnclass (value) res retain;
		string d = fs.load (path);
		if (! d) return &res;
		
		value lines = strutil::splitlines (d);
		foreach (l, lines)
		{
			const string &ln = l.sval();
			if (! ln) continue;
			if (ln[0] == '#') continue;
			if (isspace (ln[0]) && res.count())
			{
				value opts = strutil::splitspace (ln);
				foreach (o, opts) res[-1][-1]["opts"].newval() = o;
			}
			else
			{
				value words = strutil::splitspace (ln);
				if (words.count() != 8)
				{
					ferr.writeln ("Error: could not parse master.cf line:\n"
								  "> %s" %format (ln));
					exit (1);
				}
				
				res[words[0].sval()][words[1].sval()] =
					$("private", words[2]) ->
					$("unpriv", words[3]) ->
					$("chroot", words[4]) ->
					$("wakeup", words[5]) ->
					$("maxproc", words[6]) ->
					$("command", words[7]);
			}
		}
		
		return &res;
	}
	
	string *mkmastercf (const value &cf)
	{
		returnclass (string) res retain;
		
		res = "# File generated by openpanel postfix-masterconf\n"
			  "# \n"
			  "# Configuration changes to this file will be preserved, but comments may get\n"
			  "# lost when additional OpenPanel packages are installed.\n"
			  "# ===========================================================================\n"
			  "# service       type    private unpriv  chroot  wakeup  maxproc cmd+args\n"
			  "# ===========================================================================\n";
			  
		foreach (ss, cf)
		{
			foreach (s, ss)
			{
				value opts;
				if (s.exists ("opts"))
				{
					opts = s["opts"];
					s.rmval ("opts");
				}
				res.strcat ("%s\t" %format (ss.id()));
				if (ss.id().sval().strlen() < 8) res.strcat ("\t");
				res.strcat ("%s\t" %format (s.id()));
				res.strcat (s.join ("\t"));
				bool hadflag = false, onnewline = true;
				res.strcat ("\n");
				if (opts.count()) res.strcat ("  ");
				foreach (o, opts)
				{
					const string &so = o.sval();
					if (so == "-o")
					{
						if (hadflag)
						{
							onnewline = true;
							res.strcat ("\n  ");
						}
						hadflag = true;
					}
					if (! onnewline) res.strcat (' ');
					res.strcat (so);
					onnewline = false;
				}
				if (! onnewline) res.strcat ("\n");
				if (opts.count()) res.strcat ("\n");
			}
		}
		return &res;
	}
	
	int main (void)
	{
		value v = parsemastercf (argv["--conf"]);
		
		const value &args = argv["*"];
		const string &cmd = args[0];
		
		caseselector (cmd)
		{
			incaseof ("set") :
				value av = strutil::splitspace (args[2]);
				value optv;
				
				for (int i=7; i<av.count(); ++i)
					optv.newval() = av[i];
					
				v[args[1].sval()][av[0].sval()] =
					$("private", av[1]) ->
					$("unpriv", av[2]) ->
					$("chroot", av[3]) ->
					$("wakeup", av[4]) ->
					$("maxproc", av[5]) ->
					$("command", av[6]) ->
					$("opts", optv);
				
				break;
				
			defaultcase :
				break;
		}
		
		string newcf = mkmastercf (v);
		if (newcf) fs.save (argv["--conf"], newcf);
		return 0;
	}
};

$appobject (masterconf);